package cc.z8g.browser.database.bookmark;

import android.content.Context;
import android.util.Log;

import org.json.JSONException;
import org.json.JSONObject;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

import cc.z8g.browser.R;
import cc.z8g.browser.database.Bookmark;
import cc.z8g.browser.database.WebPageKt;
import cc.z8g.browser.utils.FileUtils;
import cc.z8g.browser.utils.MiscUtils;

import androidx.annotation.NonNull;

import io.reactivex.rxjava3.core.Completable;

/**
 * The class responsible for importing and exporting
 * bookmarks in the JSON format.
 */
public final class BookmarkParser {
    /** XML 格式, Chrome 浏览器导出的格式 */
    public static final int BOOKMARK_TYPE_NETSCAPE = 0;
    /** json格式 */
    public static final int BOOKMARK_TYPE_LEGACY = 1;

    private static final String TAG = "BookmarkParser";

    private static final String KEY_URL = "url";
    private static final String KEY_TITLE = "title";
    private static final String KEY_ADD_DATE = "add_date";
    private static final String KEY_ICON = "icon";
    private static final String KEY_FOLDER = "folder";
    private static final String KEY_ORDER = "order";

    private BookmarkParser() {}

    /** 配置默认的书签列表 */
    @NonNull
    public static List<Bookmark.Entry> importBookmarksFromAssets(@NonNull Context context) {
        List<Bookmark.Entry> bookmarks = new ArrayList<>();
        BufferedReader reader = null;
        try {
            InputStream inputStream = context.getResources().openRawResource(R.raw.default_bookmarks);
            reader = new BufferedReader(new InputStreamReader(inputStream));
            String line;
            while ((line = reader.readLine()) != null) {
                try {
                    JSONObject object = new JSONObject(line);
                    bookmarks.add(new Bookmark.Entry(
                                    object.getString(KEY_URL),
                                    object.getString(KEY_TITLE),
                                    object.getInt(KEY_ORDER),
                                    WebPageKt.asFolder(object.getString(KEY_FOLDER)),
                                    object.optString(KEY_ADD_DATE),
                                    object.optString(KEY_ICON)
                            )
                    );
                } catch (JSONException e) {
                    Log.e(TAG, "Can't parse line " + line, e);
                }
            }
        } catch (IOException e) {
            Log.e(TAG, "Error reading the bookmarks file", e);
        } finally {
            FileUtils.close(reader);
        }

        return bookmarks;
    }

    /**
     * Exports the list of bookmarks to an output stream.
     *
     * @param bookmarkList the bookmarks to export.
     * @param outputStream the output stream to output to.
     * @return an observable that emits a completion
     * event when the export is complete, or an error
     * event if there is a problem.
     */
    @NonNull
    public static Completable exportBookmarksToOutputStream(@NonNull final List<Bookmark.Entry> bookmarkList,
                                                            @NonNull final OutputStream outputStream) {
        return Completable.fromAction(() -> {
            Objects.requireNonNull(bookmarkList, "bookmarkList is null");
            BufferedWriter bookmarkWriter = null;
            try {
                //noinspection IOResourceOpenedButNotSafelyClosed
                bookmarkWriter = new BufferedWriter(new OutputStreamWriter(outputStream));

                JSONObject object = new JSONObject();
                for (Bookmark.Entry item : bookmarkList) {
                    object.put(KEY_TITLE, item.getTitle());
                    object.put(KEY_URL, item.getUrl());
                    object.put(KEY_FOLDER, item.getFolder().getTitle());
                    object.put(KEY_ORDER, item.getPosition());
                    bookmarkWriter.write(object.toString());
                    bookmarkWriter.newLine();
                }
            } finally {
                FileUtils.close(bookmarkWriter);
            }
        });
    }

    /**
     * Attempts to import bookmarks from the
     * given file. If the file is not in a
     * supported format, it will fail.
     *
     * @param inputStream The stream to import from.
     * @return A list of bookmarks, or throws an exception if the bookmarks cannot be imported.
     */
    @NonNull
    public static List<Bookmark.Entry> importBookmarksFromFileStream(@NonNull InputStream inputStream, int bookmarkType) throws Exception {
        if (bookmarkType == BOOKMARK_TYPE_NETSCAPE) {
            return NetscapeBookmarkImporter.importBookmarks(inputStream);
        }

        BufferedReader bookmarksReader = null;
        try {
            //noinspection IOResourceOpenedButNotSafelyClosed
            bookmarksReader = new BufferedReader(new InputStreamReader(inputStream));
            String line;

            List<Bookmark.Entry> bookmarks = new ArrayList<>();
            while ((line = bookmarksReader.readLine()) != null) {
                JSONObject object = new JSONObject(line);
                final String folderName = object.getString(KEY_FOLDER);
                final Bookmark.Entry entry = new Bookmark.Entry(
                        object.getString(KEY_URL),
                        object.getString(KEY_TITLE),
                        object.getInt(KEY_ORDER),
                        WebPageKt.asFolder(folderName),
                        object.optString(KEY_ICON),
                        object.optString(KEY_ADD_DATE)
                );
                bookmarks.add(entry);
            }

            return bookmarks;
        } finally {
            FileUtils.close(bookmarksReader);
        }
    }
}
